package com.geek688.auxiliarytool.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JTextArea;

import org.apache.commons.lang3.StringUtils;

import com.geek688.auxiliarytool.bean.TableInfo;
import com.geek688.auxiliarytool.config.Config;
import com.geek688.auxiliarytool.exception.AuxiliaryToolException;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FreeMarkUtil {
	
	private static Configuration cfg = null;
	private static String projectAbsolutePath;
	private static String packageName;
	private JTextArea area = null;
	private static List<File> filelist = new ArrayList<>();
	private String templatePath;
	private String templateName = "";
	private String outFileSuffix = "";
	
	public FreeMarkUtil(File templateFile) {
		try {
			this.templatePath = templateFile.getAbsolutePath();
			cfg = new Configuration(Configuration.VERSION_2_3_23);
			cfg.setDefaultEncoding(Config.UTF8);
			if (StringUtils.isNotBlank(templatePath) && templatePath.equals(new File("").getAbsolutePath())) {
				this.templatePath = null;
				cfg.setClassForTemplateLoading(FreeMarkUtil.class, "/template");
			}else{
				templateName = templateFile.getName();
				cfg.setDirectoryForTemplateLoading(templateFile.getParentFile());
			}
		} catch (IOException e) {
			log.error("加载模板异常",e);
		}
	}
	
	public FreeMarkUtil(String templateName) {
		this.templatePath = null;
		cfg = new Configuration(Configuration.VERSION_2_3_23);
		cfg.setDefaultEncoding(Config.UTF8);
		cfg.setClassForTemplateLoading(FreeMarkUtil.class, "/template");
		this.templateName = templateName.substring("内置模板-".length());
	}
	
	public void createModel(List<String> tablesList) {
		try {
			if (tablesList == null) {
				area.append("------------>至少选择一项"+Config.CRLF);
				return ;
			}
			area.append("------------>本次已选择 "+tablesList.size()+"个文件"+Config.CRLF+"------------>生成开始"+Config.CRLF);
			
		    long startTime = System.nanoTime();
		    
		    this.getTemplateName();
		    
		    this.getNativeFile();
		    
		    int num=0;
		   
		    for(String tableName:tablesList){
		    	long st = System.nanoTime();
		    	area.append("获取表结构信息 开始:"+Config.CRLF);
		    	List<TableInfo> data = DBUtil.getTableInfo(tableName,true);
		    	long ed = System.nanoTime();
		    	area.append("获取表结构信息 end:"+(ed-st)/1.0e9+Config.CRLF);
		    	area.append("创建文件 start:"+Config.CRLF);
		    	
				Map<String, Object> rootMap = new HashMap<>();
				this.setDataModel(rootMap, data);
				
	            this.getOutFileSuffix();
	            
	            String outName = this.getOutFileName(data);
	            
		        if(packageName.indexOf("view") !=-1) {
		        	this.createJsp(outName, rootMap);
		        }else if(packageName.indexOf("vue") !=-1) {
		        	this.createVue(outName, rootMap);
		        }else {
		        	//加载模板
			        Template temp = cfg.getTemplate(templateName);
			        File fileParentPath = this.createOutFileParentPath();
		        	createFile(fileParentPath, outName, temp, rootMap);
		        }
		        num++;
		        area.append("创建文件 end:"+(System.nanoTime()-ed)/1.0e9+Config.CRLF);
		    }
		    area.append("------------>文件生成结束，成功创建 "+num+"个"+Config.CRLF);
		    area.append("------------>总用时： "+(System.nanoTime()-startTime)/1.0e9+" ms"+Config.CRLF);
		} catch (Exception e) {
			area.append("------------>操作异常!"+Config.CRLF+templateName+Config.CRLF);
			area.append("------------>请检查 模板自定义key-value"+Config.CRLF);
			area.append(e.toString());
			log.error("操作异常",e);
		}
	}
	
	private void createVue(String outName, Map<String, Object> rootMap) throws TemplateException, IOException {
		File fileParentPath = this.createOutFileParentPath(outName);
    	String[] jspName = {"List","Add"};
    	for(String out:jspName) {
    		String outNames = "index";
    		if("Add".equals(out)) {
    			templateName = "vueAddOrEditTemplate.ftl";
    			fileParentPath = this.createOutFileParentPath(outName,"components");
    			outNames = "addOrEditModal";
    		}
    		else if("List".equals(out)) {
    			templateName = "vueIndexTemplate.ftl";
    		}
    		Template temp = cfg.getTemplate(templateName);
    		createFile(fileParentPath, outNames, temp, rootMap);
    	}
	}
	
	private void createJsp(String outName, Map<String, Object> rootMap) throws TemplateException, IOException {
		File fileParentPath = this.createOutFileParentPath(outName);
    	String[] jspName = {"List","Add","Edit"};
    	for(String out:jspName) {
    		String outNames = outName+out;
    		if("Add".equals(out))templateName = "jspAddTemplate.ftl";
    		else if("Edit".equals(out))templateName = "jspEditTemplate.ftl";
    		else if("List".equals(out))templateName = "jspListTemplate.ftl";
    		Template temp = cfg.getTemplate(templateName);
    		createFile(fileParentPath,outNames, temp, rootMap);
    	}
	}
	
	private void createFile(File fileParentPath,String outName, Template temp, Object rootMap) throws TemplateException, IOException {
		File outFile = new File(fileParentPath.getAbsolutePath()+File.separator+outName+this.outFileSuffix);
        if (!outFile.exists()) {
        	outFile.exists();
		}
        Writer docout = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),Config.UTF8));
        temp.process(rootMap, docout);
        area.append("------------>文件输出路径:"+outFile.getAbsolutePath()+Config.CRLF);
        area.append("------------>文件 "+outName+" 创建成功！"+Config.CRLF);
        area.selectAll();
	}
	
	/**
	 *  创建输出文件父级路径
	 * @param paths
	 * @return
	 */
	private File createOutFileParentPath(String... paths) {
		String rootPath = projectAbsolutePath;
        log.debug("rootPath:{}",rootPath);
        String packagePath = packageName.replaceAll("/", Config.SEPARATOR);
        log.debug("packagePath:{}",packagePath);
        StringBuffer outFileParentPath = new StringBuffer(String.format("%s%s%s", projectAbsolutePath,File.separator,packagePath));
        if (paths != null && paths.length>0) {
			for(String path:paths) {
				outFileParentPath.append(File.separator).append(path);
			}
		}
        log.info("outFileParentPath:{}",outFileParentPath.toString());
        File docFile = new File(outFileParentPath.toString());
        if (!docFile.exists()) {
			docFile.mkdirs();
			log.info("路径创建成功!");
		}
        return docFile;
	}
	
	/**
	 *  设置freemark dataModel
	 * @param rootMap
	 * @param data
	 * @return
	 */
	private void setDataModel(Map<String, Object> rootMap,List<TableInfo> data) {
		if (packageName.contains("action")) {
			rootMap.put("requestPrefix", this.getRequestPrefix("action"));
		}else if (packageName.contains("view")) {
			rootMap.put("requestPrefix", this.getRequestPrefix("view"));
		}else if (packageName.contains("vue")) {
			rootMap.put("requestPrefix", this.getRequestPrefix("vue"));
		}
		rootMap.put("tablePrefix", data.get(0).getTablePrefix());
		rootMap.put("tableSubName", data.get(0).getTableSubName());
		rootMap.put("package", packageName.replaceAll("/", "."));
        rootMap.put("columns", data); 
        rootMap.put("pkName", data.get(0).getPk());
        rootMap.put("tableName", data.get(0).getTableName());
        rootMap.put("columnsSize", data.size());
        rootMap.put("creatTime", new Date());
        //加载自定义key-value
        LogUtil.readFreeMarkSet().forEach(markBean->{
        	rootMap.put(markBean.getKey(), markBean.getValue());
        });
        if(rootMap.get("description")==null) {
        	rootMap.put("description", data.get(0).getTableSubName());
        }
        if(rootMap.get("description")!=null) {
        	rootMap.put("description", data.get(0).getTableSubName()+rootMap.get("description").toString());
        }
        if (packageName.indexOf("service") !=-1 || packageName.indexOf("action") !=-1) {
        	String modelPackage = FreeMarkUtil.getPackage(data.get(0).getTableName(),false);
        	String servicePackage = FreeMarkUtil.getPackage(data.get(0).getClassName()+Config.SERVICE,true);
			rootMap.put("modelPackage", modelPackage);
            rootMap.put("servicePackage", servicePackage);
		}
        rootMap.put("className", data.get(0).getClassName());
	}
	
	/**
	 *  获取输出文件名
	 * @param className
	 * @return 
	 * @return
	 */
	private String getOutFileName(List<TableInfo> data) {
		String outName = "";
		if ((templateName.indexOf("action") !=-1 || templateName.indexOf("controller") !=-1)|| packageName.indexOf("action") !=-1 ) {
			outName = data.get(0).getClassName();
		}else if (templateName.indexOf("service") !=-1 || packageName.indexOf("service") !=-1 ) {
			outName = data.get(0).getClassName();
		}else if (templateName.indexOf("model") !=-1 || packageName.indexOf("model") !=-1 ) {
			outName = data.get(0).getTableName();
		}else if (templateName.indexOf("view") !=-1 || packageName.indexOf("view") !=-1 ){
			outName = data.get(0).getTableSubName();
			outName = outName.substring(0,1).toLowerCase()+outName.substring(1);
		}else if (templateName.indexOf("vue") !=-1 || packageName.indexOf("vue") !=-1 ){
			outName = data.get(0).getTableSubName();
			outName = outName.substring(0,1).toLowerCase()+outName.substring(1);
		}
	    log.info("输出文件名:{}",outName);
	    return outName;
	}
	
	/**
	 *  获取输出文件后缀名
	 * @param className
	 * @return
	 */
	private void getOutFileSuffix() {
		if ((templateName.indexOf("action") !=-1 || templateName.indexOf("controller") !=-1)|| packageName.indexOf("action") !=-1 ) {
        	this.outFileSuffix = String.format("%s%s", Config.ACTION,Config.SUFFIX_JAVA);
		}else if (templateName.indexOf("service") !=-1 || packageName.indexOf("service") !=-1 ) {
			this.outFileSuffix = String.format("%s%s", Config.SERVICE,Config.SUFFIX_JAVA);
		}else if (templateName.indexOf("model") !=-1 || packageName.indexOf("model") !=-1 ) {
			this.outFileSuffix = Config.SUFFIX_JAVA;
		}else if (templateName.indexOf("view") !=-1 || packageName.indexOf("view") !=-1 ){
			this.outFileSuffix = Config.SUFFIX_JSP;
		}else if(templateName.indexOf("vue") !=-1 || packageName.indexOf("vue") !=-1) {
			this.outFileSuffix = Config.SUFFIX_VUE;
		}
	    log.info("输出文件后缀名:{}",this.outFileSuffix);
	}
	
	/**
	 * 根据类名获取该类的包名
	 * @param className
	 * @return
	 */
	public static String getPackage(String className,boolean flag) {
		String resultName = "";
		String packageSub = "";
		if (packageName.contains("action")) {
			packageSub = FreeMarkUtil.packageName.substring(packageName.indexOf("action")+"action".length());
		}
		if (packageName.contains("service")) {
			packageSub = FreeMarkUtil.packageName.substring(packageName.indexOf("service")+"service".length());
		}
		for(File file:filelist){
			String parent = file.getParent();
			log.debug("parent-->"+parent);
			String _package = "";
			if (!parent.equals(projectAbsolutePath)) {
				log.debug("projectAbsolutePath-->"+projectAbsolutePath);
				_package = parent.substring(projectAbsolutePath.length()+1);
			}
			log.debug("_package-->"+_package);
			_package = _package.replace("\\", ".");
			String fileName = file.getName().substring(0,file.getName().length()-5);
			log.debug("fileName-->"+fileName);
			if(packageName.indexOf("action.") !=-1) {
				//优先查找包名前缀一致的
				if (flag && _package.endsWith(packageSub) && className.equals(fileName)) {
					resultName = _package+"."+fileName;break;
				}
				//包名前缀不一致 但类文件名一致
				if (flag && !"".equals(resultName) && className.equals(fileName)) {
					resultName = _package+"."+fileName;break;
				}
			}else {
				if (className.equals(fileName)) {
					resultName = _package+"."+fileName;break;
				}
			}
			if (!flag && className.equals(fileName)) {
				resultName = _package+"."+fileName;break;
			}
		}
		return resultName;
	}
	/**
	 * 获取请求前缀
	 * @param pattern
	 * @return
	 */
	private String getRequestPrefix(String pattern) {
		String requestPrefix = "";
		if (FreeMarkUtil.packageName.indexOf(pattern)!=-1) {
			requestPrefix = FreeMarkUtil.packageName.substring(FreeMarkUtil.packageName.indexOf(pattern)+pattern.length());
			if (requestPrefix.endsWith("/")) {
				requestPrefix = requestPrefix.substring(0, requestPrefix.length()-1);
			}
		}
	    log.info("请求前缀:{}",requestPrefix);
	    return requestPrefix;
	}
	/**
	 * 扫描当前项目路径下全部.java文件
	 * @param 
	 * @return
	 */
	private void getNativeFile() throws IOException {
		if (packageName.indexOf("service") !=-1 || packageName.indexOf("action") !=-1|| packageName.indexOf("view") !=-1) {
	    	long ed = System.nanoTime();
	    	area.append("------------>扫描文件 start:"+Config.CRLF);
	    	filelist = FileUtil.getFileList(projectAbsolutePath,Config.SUFFIX_JAVA);
	    	area.append("------------>扫描文件 end:"+(System.nanoTime()-ed)/1.0e9+Config.CRLF);
	    }
	}
	/**
	 *  获取模板名称
	 * @param 
	 * @return
	 */
	private void getTemplateName() {
		if (StringUtils.isNotBlank(templatePath)||StringUtils.isNotBlank(templateName)) {
	    	return ;
	    }
    	area.append(Config.CRLF+"------------>未指定模板路径，默认加载内置模板！"+Config.CRLF);
	    if (packageName.indexOf("action") !=-1 ) {
			templateName = "springControllerTemplate.ftl";
		}else if (packageName.indexOf("service") !=-1 ) {
			templateName = "springServiceTemplate.ftl";
		}else if (packageName.indexOf("model") !=-1 ) {
			templateName = "springModelTemplate.ftl";
		}
	    if (packageName.indexOf("view") ==-1 && packageName.indexOf("vue") ==-1) {
	    	area.append("------------>内置模板加载失败，请检查包名格式！"+Config.CRLF);
	    	area.append("------------>建议包名格式 xxx.xxx.model.xxx"+Config.CRLF);
	    	area.append("------------>建议包名格式 xxx.xxx.service.xxx"+Config.CRLF);
	    	area.append("------------>建议包名格式 xxx.xxx.action.xxx"+Config.CRLF);
	    	area.append("------------>建议包名格式 view.xxx.xxx.xxx"+Config.CRLF);
	    	throw new AuxiliaryToolException("内置模板加载失败");
		}
	    area.append("------------>将使用"+templateName+"模板！"+Config.CRLF+Config.CRLF);
	}
	
	public void setArea(JTextArea area) {
		this.area = area;
	}

	public void setProjectAbsolutePath(File projectAbsoluteFile) {
		FreeMarkUtil.projectAbsolutePath = projectAbsoluteFile.getAbsolutePath();
	}

	public void setPackagePath(String packageName) {
		FreeMarkUtil.packageName = packageName.replaceAll("(\\.+)|(\\\\+)", "/");
	}
	
}
